package org.goodCompany.sound.encoders;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import java.io.File;
import java.io.InputStream;

/**
 * @author trollswagen
 *         Date: Jan 6, 2007
 *         Time: 2:43:16 PM
 */
public class Mp3Encoder {
    private static final Log log = LogFactory.getLog(Mp3Encoder.class);

    private static final AudioFormat.Encoding MPEG1L3 = new Encoding("MPEG1L3");
    private static final AudioFileFormat.Type MP3 = new AudioFileFormatType("MP3", "mp3");

    private String sourceFileName;
    private String targetFileName;

    /*
    * Public Getter/Setter Methods
    */
    public String getSourceFileName() {
        return sourceFileName;
    }

    public void setSourceFileName(String sourceFileName) {
        this.sourceFileName = sourceFileName;
    }

    public String getTargetFileName() {
        return targetFileName;
    }

    public void setTargetFileName(String targetFileName) {
        this.targetFileName = targetFileName;
    }

    /*
    * Public Methods
    */
    public void encode() throws Exception {
        AudioInputStream sourceStream = getSourceStream();
        AudioInputStream targetStream = getTargetStream(sourceStream);

        if (targetStream != null) {
            AudioSystem.write(targetStream, MP3, new File(this.targetFileName));

            log.info("Effective parameters of output file:");
            log.info("  Version      = " + System.getProperty("tritonus.lame.encoder.version", ""));
            log.info("  Quality      = " + System.getProperty("tritonus.lame.effective.quality", "<none>"));
            log.info("  Bitrate      = " + System.getProperty("tritonus.lame.effective.bitrate", "<none>"));
            log.info("  Channel Mode = " + System.getProperty("tritonus.lame.effective.chmode", "<none>"));
            log.info("  VBR mode     = " + System.getProperty("tritonus.lame.effective.vbr", "<none>"));
            log.info("  Sample rate  = " + System.getProperty("tritonus.lame.effective.samplerate", "<none>"));
            log.info("  Encoding     = " + System.getProperty("tritonus.lame.effective.encoding", "<none>"));
        } else {
            log.warn("encode(): unable to write mp3");
        }
    }

    /*
    * Private Methods
    */
    private AudioInputStream getSourceStream() throws Exception {
        InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(this.sourceFileName);
        return AudioSystem.getAudioInputStream(inputStream);
    }

    private AudioInputStream getTargetStream(AudioInputStream sourceStream) {
        AudioFormat sourceFormat = sourceStream.getFormat();
        log.info("getTargetStream(): source format: " + sourceFormat);

        if (AudioSystem.isConversionSupported(MPEG1L3, sourceFormat)) {
            return getMp3Stream(sourceStream);
        }


        log.info("getTargetStream(): direct conversion not possible.  Trying with intermediate PCM format.");
        AudioFormat pcmAudioFormat = getPcmAudioFormat(sourceFormat);
        if (AudioSystem.isConversionSupported(pcmAudioFormat, sourceFormat)) {
            return getMp3Stream(AudioSystem.getAudioInputStream(pcmAudioFormat, sourceStream));
        }

        log.info("getTargetStream(): intermediate conversion not possible.");

        return null;
    }

    private AudioFormat getPcmAudioFormat(AudioFormat sourceFormat) {
        return new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
                sourceFormat.getSampleRate(), 16, sourceFormat.getChannels(),
                2 * sourceFormat.getChannels(), // frameSize
                sourceFormat.getSampleRate(), false);
    }

    private AudioInputStream getMp3Stream(AudioInputStream sourceStream) {
        AudioInputStream targetStream = AudioSystem.getAudioInputStream(MPEG1L3, sourceStream);
        log.info("getMp3Stream(): converted: " + targetStream.getClass().getName());
        log.info("getMp3Stream(): output format: " + targetStream.getFormat());
        return targetStream;
    }

    private static class Encoding extends AudioFormat.Encoding {

        public Encoding(String name) {
            super(name);
        }
    }

    private static class AudioFileFormatType extends AudioFileFormat.Type {

        public AudioFileFormatType(String name, String extension) {
            super(name, extension);
        }
    }
}
